using System;
using System.Collections.Generic;
using System.Text;

using System.Xml;
using System.Reflection;

namespace ClientGUI
{
    /// <summary>
    /// Connects an xml document with in memory types.
    /// </summary>
	public class Configuration
	{
		Dictionary<String, ModuleConfig> my_configs;
        List<ModuleConfig> my_list_configs;
        //These contain the same data, one is just ordered to index with nums
        //and the other with strings

        /// <summary>
        /// Setup a Configuration from a specified xml document to configure it.
        /// </summary>
        /// <param name="config"></param>
		public Configuration(XmlDocument config)
		{
			my_configs = new Dictionary<string, ModuleConfig>();
            my_list_configs = new List<ModuleConfig>();
			XmlNodeList tags = config.SelectNodes("Config/Tags/Tag");
			for (int i = 0; i < tags.Count; ++i) {
				String type = tags[i].
					Attributes.GetNamedItem("type").Value;
				String name = tags[i].
					Attributes.GetNamedItem("name").Value;


				Type mod_type = Type.GetType("ClientGUI." + type);
				my_configs[name] = new ModuleConfig(name, mod_type,
					tags[i].SelectSingleNode(type),
                    tags[i].SelectSingleNode("Config"));
                my_list_configs.Add(my_configs[name]);
			}
		}

        /// <summary>
        /// Length if this Configuration.
        /// </summary>
        public int Length { get { return my_list_configs.Count; } }

        /// <summary>
        /// Get the moduleconfig at a spefied index.  In the range (0, Length]
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        public ModuleConfig this[int index]
        {
            get
            {
                return my_list_configs[index];
            }
        }


        /// <summary>
        /// Gets the configuration object associated with a module
        /// named "name"
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public ModuleConfig GetModuleConfig(String name)
        {
            return my_configs[name];
        }

        /// <summary>
        /// Grabs am instance of the proper type for "name"
        /// </summary>
        /// <param name="name">The module type name</param>
        /// <param name="doc">The chunk of the document the instance
        /// corresponds to</param>
        /// <param name="parent">The parent you will attach this instance to
        /// </param>
        /// <param name="host">The display host</param>
        /// <returns>a new instance</returns>
		public Module GetInstance(String name, XmlNode doc, 
			Module parent, DisplayDocument host)
		{
			return my_configs[name].GetInstance(doc, parent, host);
		}
	}

    /// <summary>
    /// Represents a configuration for one type of module.
    /// </summary>
	public class ModuleConfig
	{
		String my_name;
		Type my_type;
		XmlNode my_modconfig;
        TagConfig my_config;
        /// <summary>
        /// A constructor.
        /// </summary>
        /// <param name="modName"></param>
        /// <param name="modClass"></param>
        /// <param name="modconfig"></param>
        /// <param name="config"></param>
        public ModuleConfig(String modName,
                            Type modClass,
                            XmlNode modconfig,
                            XmlNode config)
        {
            my_name = modName;
            my_type = modClass;
            my_modconfig = modconfig;
            my_config = new TagConfig(config);
        }

        /// <summary>
        /// The name of this setting.
        /// </summary>
        public String Name
        {
            get
            {
                return my_name ;
            }
        }

        /// <summary>
        /// The number of KeyValuePair in this
        /// </summary>
        public int Length
        {
            get
            {
                if (my_modconfig == null)
                    return 0;
                if (my_modconfig.ChildNodes == null)
                    return 0;
                return my_modconfig.ChildNodes.Count;
            }
        }


        /// <summary>
        /// This is a setting.
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        public KeyValuePair<String, String> this[int index]
        {
            set
            {
                my_modconfig.ChildNodes[index].Attributes[0].Value = value.Value;
            }
            get
            {
                KeyValuePair<String, String> pair = new KeyValuePair<string, string>(
                    my_modconfig.ChildNodes[index].Name + " " + 
                                my_modconfig.ChildNodes[index].Attributes[0].Name,
                    my_modconfig.ChildNodes[index].Attributes[0].Value);
                return pair;
            }
        }

        /// <summary>
        /// The TagConfig for this module, see TagConfig.
        /// </summary>
        public TagConfig TagConfig { get { return my_config; } }

        /// <summary>
        /// Get a module for the specified document node, 
        /// </summary>
        /// <param name="doc">Document to create an instance for</param>
        /// <param name="parent">Parent of the resulting Module</param>
        /// <param name="host">Control that displays this Module</param>
        /// <returns></returns>
		public Module GetInstance(XmlNode doc, Module parent, 
			DisplayDocument host)
		{
			Type[] types = new Type[5];
			types[0] = doc.GetType();
			types[3] = doc.GetType();
            types[4] = Type.GetType("ClientGUI.TagConfig");
			types[1] = Type.GetType("ClientGUI.Module");
			types[2] = host.GetType(); ;
			ConstructorInfo const_info = my_type.GetConstructor(types);
			object[] parameters = new object[5];
			parameters[0] = doc;
			parameters[3] = my_modconfig;
            parameters[4] = my_config;
			parameters[1] = parent;
			parameters[2] = host;
			return const_info.Invoke(parameters) as Module;
		}
	}

}
